﻿using System;
using System.Collections.Generic;
using System.Linq;
using HK.Core.Common.Data;
using HK.Core.Extend;
using HK.Editor.Common;
using HK.Editor.GUIEx.Protocol.TreeView.Control;
using HK.Editor.GUIEx.Protocol.TreeView.Data;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
using UTreeView = UnityEditor.IMGUI.Controls.TreeView;
using HLogger = HK.Core.Logs.Logger;

namespace HK.Editor.GUIEx.TreeView.Control
{

    /// <summary>
    /// 表树Null异常
    /// </summary>
    public class TableTreeNullReferenceException : NullReferenceException
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="iMessage">Message</param>
        public TableTreeNullReferenceException(string iMessage) : base(iMessage) {}
    }
    
    /// <summary>
    /// 表树参数异常
    /// </summary>
    public class TableTreeArgumentException : ArgumentException
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="iParam">参数</param>
        /// <param name="iMessage">Message</param>
        public TableTreeArgumentException(string iParam, string iMessage) : base(iParam, iMessage) {}
    }
    
    /// <summary>
    /// 委托事件定义 : 绘制TableTree的列
    /// </summary>
    /// <param name="iRect">绘制矩形范围</param>
    /// <param name="iBranch">树枝数据信息</param>
    /// <param name="iRowIndex">行索引</param>
    /// <param name="iColumnIndex">列索引</param>
    /// <typeparam name="TBranch">树枝类型</typeparam>
    public delegate void DrawTableTreeColumn<in TBranch>(Rect iRect, TBranch iBranch, int iRowIndex, int iColumnIndex)
        where TBranch : JsonData, ITreeBranch;
    
    /// <summary>
    /// 表树
    /// </summary>
    /// <typeparam name="TRow">树形 - 行类型</typeparam>
    /// <typeparam name="TBranch">树枝数据类型</typeparam>
    public abstract class TableTree<TRow, TBranch> : UTreeView, ITableTree<TRow, TBranch>
        where TRow : TreeViewItem, ITreeViewRow<TBranch>
        where TBranch : JsonData, ITreeBranch
    {
        /// <summary>
        /// 容量
        /// </summary>
        public int Capacity { get; private set; } = 100;

        /// <summary>
        /// 行列表
        /// </summary>
        public List<TreeViewItem> Rows { get; private set; } = null;
        
        /// <summary>
        /// Tree Model
        /// </summary>
        public ITableTreeDataContainer<TBranch> DataContainer { get; private set; } = null;

        /// <summary>
        /// 检索Key
        /// </summary>
        public string SearchKey
        {
            get => searchString; 
            set => searchString = value;
        }
        
        public event Action<IList<TreeViewItem>>  beforeDroppingDraggedItems;
        
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="iState">State</param>
        /// <param name="iDataContainer">数据容器</param>
        /// <param name="iSortColumns">列排序回调</param>
        /// <param name="iDrawColumns">绘制列回调</param>
        /// <param name="iFoldoutColIndex">折叠箭头所在列索引</param>
        protected TableTree(TreeViewState iState, ITableTreeDataContainer<TBranch> iDataContainer,
            SortTableTree iSortColumns, DrawTableTreeColumn<TBranch> iDrawColumns, int iFoldoutColIndex) : base(iState)
        {
            InitSettings (iDataContainer, iSortColumns, iDrawColumns, iFoldoutColIndex);
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="iState">State</param>
        /// <param name="iHeader">Header</param>
        /// <param name="iDataContainer">数据容器</param>
        /// <param name="iSortColumns">列排序回调</param>
        /// <param name="iDrawColumns">绘制列回调</param>
        /// <param name="iFoldoutColIndex">折叠箭头所在列索引</param>
        protected TableTree(TreeViewState iState, MultiColumnHeader iHeader, ITableTreeDataContainer<TBranch> iDataContainer,
            SortTableTree iSortColumns, DrawTableTreeColumn<TBranch> iDrawColumns, int iFoldoutColIndex) : 
            base(iState, iHeader)
        {
            InitSettings (iHeader, iDataContainer, iSortColumns, iDrawColumns, iFoldoutColIndex);
        }
        
        /// <summary>
        /// 初始化设定
        /// </summary>
        /// <param name="iDataContainer">数据容器</param>
        /// <param name="iSortColumns">列排序回调</param>
        /// <param name="iDrawColumns">绘制列回调</param>
        /// <param name="iFoldoutColIndex">折叠箭头所在列索引</param>
        private void InitSettings (ITableTreeDataContainer<TBranch> iDataContainer,
            SortTableTree iSortColumns, DrawTableTreeColumn<TBranch> iDrawColumns, 
            int iFoldoutColIndex)
        {
            Rows = new List<TreeViewItem>(Capacity);
            
            // 事件
            DataContainer = iDataContainer;
            DataContainer.Changed += OnTableTreeChanged;
            // 列排序回调
            SortColumns = iSortColumns;
            // 绘制列
            DrawColumn = iDrawColumns;
            
            // 行高设定
            rowHeight = RowHeight;
            // 折叠箭头所在列索引
            columnIndexForTreeFoldouts = 0 >= iFoldoutColIndex ? FoldoutColIndex : iFoldoutColIndex;
            // 行背景颜色交替
            showAlternatingRowBackgrounds = AlternatingRowBackgrounds;
            showBorder = true;
            // 折叠箭头在行中垂直偏移
            customFoldoutYOffset = (RowHeight - EditorGUIUtility.singleLineHeight) * 0.5f;
            // 在树标签前留出空间，以便显示开关按钮
            extraSpaceBeforeIconAndLabel = EditorConst.ToggleWidth;
        }

        /// <summary>
        /// 初始化设定
        /// </summary>
        /// <param name="iHeader">头部</param>
        /// <param name="iDataContainer">数据容器</param>
        /// <param name="iSortColumns">列排序回调</param>
        /// <param name="iDrawColumns">绘制列回调</param>
        /// <param name="iFoldoutColIndex">折叠箭头所在列索引</param>
        private void InitSettings (MultiColumnHeader iHeader, ITableTreeDataContainer<TBranch> iDataContainer,
            SortTableTree iSortColumns, DrawTableTreeColumn<TBranch> iDrawColumns, 
            int iFoldoutColIndex)
        {
            // 初始化设定
            InitSettings(iDataContainer, iSortColumns, iDrawColumns, iFoldoutColIndex);
            
            // 头部排序事件
            iHeader.sortingChanged += OnHeaderSortChanged;
        }

#region Events
        
        private TableTreeChanged _onChanged = () =>
        {
#if UNITY_EDITOR
            // var descTmp = string.IsNullOrEmpty(desc) ? "-" : desc;
            // HLogger.Info($"AbManager::OnStateChanged():{state}({target.Type}) - {target.Key}({descTmp})");
#endif
        };

        private TableTreeChanged _changed;

        /// <summary>
        /// 变更事件
        /// </summary>
        public event TableTreeChanged Changed
        {

            add
            {
#if UNITY_EDITOR
                if (null != value)
                {
                    HLogger.Info($"Changed(): + {value.Target.GetType().FullName}:{value.Method.Name}");
                }
#endif
                _onChanged += value;
            }
            remove
            {

#if UNITY_EDITOR
                if (null != value)
                {
                    HLogger.Info($"Changed(): - {value.Target.GetType().FullName}:{value.Method.Name}");
                }
#endif
                if (null != _onChanged) _onChanged -= value;
            }
        }
        
        /// <summary>
        /// 列排序回调
        /// </summary>
        public SortTableTree SortColumns { get; private set; } = null;

        /// <summary>
        /// 绘制列
        /// </summary>
        public DrawTableTreeColumn<TBranch> DrawColumn { get; private set; } = null;
        
        /// <summary>
        /// 树结构变更事件
        /// </summary>
        private void OnTableTreeChanged ()
        {
            _onChanged?.Invoke();
            Reload ();
        }

        /// <summary>
        /// 头部排序变更事件
        /// </summary>
        /// <param name="iHeader">表头部</param>
        private void OnHeaderSortChanged(MultiColumnHeader iHeader)
        {
            Sort(rootItem, GetRows());
        }

        /// <summary>
        /// 校验列是否可排序
        /// </summary>
        /// <param name="iColIndex">列索引</param>
        /// <returns>true:可排序; false:不可排序;</returns>
        protected virtual bool IsColSortable(int iColIndex) => true;

        /// <summary>
        /// 排序
        /// </summary>
        /// <param name="iRoot">根节点</param>
        /// <param name="iRows">行列表</param>
        protected virtual void Sort(TreeViewItem iRoot, IList<TreeViewItem> iRows)
        {
            // 只有一行
            if (1 >= iRows.Count) return;
			
            // 不可排序
            if (!IsColSortable(multiColumnHeader.sortedColumnIndex))
            {
                return;
            }
			
            // 根据列排序
            SortColumns?.Invoke(multiColumnHeader, rootItem);
            
            // TableTree转换成List
            TableTreeToList(iRoot, iRows);
            
            // 重绘
            Repaint();
        }

        /// <summary>
        /// TableTree转换成List
        /// </summary>
        /// <param name="iRoot">根节点</param>
        /// <param name="iRows">行列表</param>
        protected virtual void TableTreeToList(TreeViewItem iRoot, IList<TreeViewItem> iRows)
        {
            if (null == iRoot)
            {
                throw new TableTreeNullReferenceException("iRoot");
            }

            if (null == iRows)
            {
                throw new TableTreeNullReferenceException("iRows");
            }
            iRows.Clear();
	
            if (null == iRoot.children) return;

            var stack = new Stack<TreeViewItem>();
            for (var idx = iRoot.children.Count - 1; idx >= 0; --idx)
            {
                stack.Push(iRoot.children[idx]);
            }

            while (stack.Count > 0)
            {
                var current = stack.Pop();
                iRows.Add(current);

                if (current.hasChildren && null !=  current.children[0])
                {
                    for (var idx = current.children.Count - 1; idx >= 0; --idx)
                    {
                        stack.Push(current.children[idx]);
                    }
                }
            }
        }
        
#endregion
        
#region Size&Rect

        /// <summary>
        /// 行高
        /// </summary>
        /// <returns>行高</returns>
        protected virtual float RowHeight => EditorConst.TreeViewRowHeight;

        /// <summary>
        /// 可折叠得列索引
        /// <para>* 从0开始的列索引，默认第一列</para>
        /// </summary>
        protected virtual int FoldoutColIndex => 0;

        /// <summary>
        /// 行背景颜色交替标志位
        /// </summary>
        protected virtual bool AlternatingRowBackgrounds => true;

#endregion

#region Build
        
        /// <summary>
        /// Build Root
        /// </summary>
        /// <returns></returns>
        protected override TreeViewItem BuildRoot()
        {
            return new TreeViewRow<TBranch>(DataContainer.Root.Id, -1, DataContainer.Root.Name, DataContainer.Root);
        }
        
        /// <summary>
        /// Build Rows
        /// </summary>
        /// <param name="iRoot">根节点</param>
        /// <returns></returns>
        protected override IList<TreeViewItem> BuildRows(TreeViewItem iRoot)
        {
            if (null == DataContainer.Root)
            {
                this.Error("BuildRows():tree model root is null. did you call SetData()?");
                return null;
            }

            Rows.Clear ();
            if (!string.IsNullOrEmpty(searchString))
            {
                Search (DataContainer.Root, searchString, Rows);
            }
            else
            {
                if (DataContainer.Root.HasChildren)
                {
                    AddChildren(DataContainer.Root, 0, Rows);
                }
            }

            // We still need to setup the child parent information for the rows since this 
            // information is used by the TreeView internal logic (navigation, dragging etc)
            SetupParentsAndChildrenFromDepths (iRoot, Rows);

            return Rows;
        }
        
        /// <summary>
        /// 行Gui
        /// </summary>
        /// <param name="iRowArgs">行参数</param>
        protected override void RowGUI(RowGUIArgs iRowArgs)
        {
            var row = (TreeViewRow<TBranch>) iRowArgs.item;
            
            for (var idx = 0; idx < iRowArgs.GetNumVisibleColumns (); ++idx)
            {
                DrawColumnsGUI(iRowArgs.GetCellRect(idx), row, iRowArgs.GetColumn(idx), ref iRowArgs);
            }
        }

        /// <summary>
        /// 绘制列Gui
        /// </summary>
        /// <param name="iRect">绘制举行区域</param>
        /// <param name="iRow">行元素</param>
        /// <param name="iColumnIndex">列索引</param>
        /// <param name="iRowArgs">行参数</param>
        protected virtual void DrawColumnsGUI(Rect iRect, TreeViewRow<TBranch> iRow, 
            int iColumnIndex, ref RowGUIArgs iRowArgs)
        {
            CenterRectUsingSingleLineHeight(ref iRect);

            // 当前为折叠缩进列
            var colRect = iRect;
            if (columnIndexForTreeFoldouts == iColumnIndex)
            {
                colRect.x += GetContentIndent(iRow);
            }
            DrawColumn?.Invoke(colRect, iRow.Data, iRowArgs.row, iColumnIndex);
        }

#endregion
        
        /// <summary>
        /// 追加子节点
        /// </summary>
        /// <param name="iParent">父系欸但</param>
        /// <param name="iDepth">深度</param>
        /// <param name="iNewRows">新行列表</param>
        private void AddChildren (ITreeBranch iParent, int iDepth, IList<TreeViewItem> iNewRows)
        {
            foreach (var child in iParent.Children)
            {
                var row = new TreeViewRow<TBranch>(child.Id, iDepth, child.Name, child as TBranch);
                iNewRows.Add(row);

                if (child.HasChildren)
                {
                    if (IsExpanded(child.Id))
                    {
                        AddChildren (child, iDepth + 1, iNewRows);
                    }
                    else
                    {
                        row.children = CreateChildListForCollapsedParent();
                    }
                }
            }
        }
        
        void Search(TBranch iSearchBranch, string iSearch, List<TreeViewItem> iResult)
        {
            if (string.IsNullOrEmpty(iSearch))
            {
                throw new TableTreeArgumentException("search", "Invalid search: cannot be null or empty");
            }

            const int kItemDepth = 0; // tree is flattened when searching

           var stack = new Stack<ITreeBranch>();
           foreach (var branch in iSearchBranch.Children)
           {
               stack.Push(branch);
           }
            while (stack.Count > 0)
            {
                var current = stack.Pop();
                // Matches search?
                if (current.Name.IndexOf(iSearch, StringComparison.OrdinalIgnoreCase) >= 0)
                {
                    iResult.Add(new TreeViewRow<TBranch>(current.Id, kItemDepth, current.Name, current as TBranch));
                }

                if (current.Children != null && current.Children.Count > 0)
                {
                    foreach (var child in current.Children)
                    {
                        stack.Push(child);
                    }
                }
            }
            SortSearchResult(iResult);
        }

        private void SortSearchResult (List<TreeViewItem> rows)
        {
            rows.Sort ((x,y) => EditorUtility.NaturalCompare (x.displayName, y.displayName)); // sort by displayName by default, can be overriden for multicolumn solutions
        }
	
        protected override IList<int> GetAncestors (int id)
        {
            return DataContainer.GetAncestorIds(id);
        }

        protected override IList<int> GetDescendantsThatHaveChildren (int id)
        {
            return DataContainer.GetDescendantsThatHaveChildren(id);
        }

#region Drag

        const string k_GenericDragID = "GenericDragColumnDragging";

        protected override bool CanStartDrag (CanStartDragArgs args)
        {
	        return true;
        }

        protected override void SetupDragAndDrop(SetupDragAndDropArgs args)
        {
	        if (hasSearch)
		        return;

	        DragAndDrop.PrepareStartDrag();
	        var draggedRows = GetRows().Where(item => args.draggedItemIDs.Contains(item.id)).ToList();
	        DragAndDrop.SetGenericData(k_GenericDragID, draggedRows);
	        DragAndDrop.objectReferences = new UnityEngine.Object[] { }; // this IS required for dragging to work
	        string title = draggedRows.Count == 1 ? draggedRows[0].displayName : "< Multiple >";
	        DragAndDrop.StartDrag (title);
        }

        protected override DragAndDropVisualMode HandleDragAndDrop (DragAndDropArgs args)
        {
	        // Check if we can handle the current drag data (could be dragged in from other areas/windows in the editor)
	        var draggedRows = DragAndDrop.GetGenericData(k_GenericDragID) as List<TreeViewItem>;
	        if (draggedRows == null)
		        return DragAndDropVisualMode.None;

	        // Parent item is null when dragging outside any tree view items.
	        switch (args.dragAndDropPosition)
	        {
		        case DragAndDropPosition.UponItem:
		        case DragAndDropPosition.BetweenItems:
			        {
				        var validDrag = ValidDrag(args.parentItem, draggedRows);
				        if (args.performDrop && validDrag)
				        {
					        var parentData = (args.parentItem as TRow)?.Data;
					        OnDropDraggedElementsAtIndex(draggedRows, parentData, args.insertAtIndex == -1 ? 0 : args.insertAtIndex);
				        }
				        return validDrag ? DragAndDropVisualMode.Move : DragAndDropVisualMode.None;
			        }

		        case DragAndDropPosition.OutsideItems:
			        {
				        if (args.performDrop)
					        OnDropDraggedElementsAtIndex(draggedRows, DataContainer.Root, DataContainer.Root.ChildCount);

				        return DragAndDropVisualMode.Move;
			        }
		        default:
			        Debug.LogError("Unhandled enum " + args.dragAndDropPosition);
			        return DragAndDropVisualMode.None;
	        }
        }

        public void OnDropDraggedElementsAtIndex (List<TreeViewItem> draggedRows, ITreeBranch parent, int insertIndex)
        {
	        if (beforeDroppingDraggedItems != null)
		        beforeDroppingDraggedItems (draggedRows);

	        var draggedElements = new List<ITreeBranch> ();
            foreach (var x in draggedRows)
            {
                draggedElements.Add ((x as TRow).Data);
            }

	        var selectedIDs = draggedElements.Select (x => x.Id).ToArray();
            DataContainer.MoveBranches(parent, insertIndex, draggedElements);
	        SetSelection(selectedIDs, TreeViewSelectionOptions.RevealAndFrame);
        }


        bool ValidDrag(TreeViewItem parent, List<TreeViewItem> draggedItems)
        {
	        TreeViewItem currentParent = parent;
	        while (currentParent != null)
	        {
		        if (draggedItems.Contains(currentParent))
			        return false;
		        currentParent = currentParent.parent;
	        }
	        return true;
        }

#endregion
        
        /// <summary>
        /// 刷新
        /// </summary>
        /// <param name="iDataContainer">数据容器</param>
        public void Refresh(ITableTreeDataContainer<TBranch> iDataContainer)
        {
            DataContainer = iDataContainer;
            Reload();
        }
        
    }
    
    /// <summary>
    /// Tree View Model
    /// </summary>
    /// <typeparam name="TBranch">树形数据类型</typeparam>
    public abstract class TableTree<TBranch> : TableTree<TreeViewRow<TBranch>, TBranch> 
        where TBranch : JsonData, ITreeBranch
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="iState">State</param>
        /// <param name="iDataContainer">数据容器</param>
        /// <param name="iSortColumns">列排序回调</param>
        /// <param name="iDrawColumns">绘制列回调</param>
        /// <param name="iFoldoutColIndex">折叠箭头所在列索引</param>
        protected TableTree(TreeViewState iState, ITableTreeDataContainer<TBranch> iDataContainer,
            SortTableTree iSortColumns, DrawTableTreeColumn<TBranch> iDrawColumns, int iFoldoutColIndex) : 
            base(iState, iDataContainer, iSortColumns, iDrawColumns, iFoldoutColIndex) { }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="iState">State</param>
        /// <param name="iHeader">Header</param>
        /// <param name="iDataContainer">数据容器</param>
        /// <param name="iSortColumns">列排序回调</param>
        /// <param name="iDrawColumns">绘制列回调</param>
        /// <param name="iFoldoutColIndex">折叠箭头所在列索引</param>
        protected TableTree(TreeViewState iState, MultiColumnHeader iHeader, ITableTreeDataContainer<TBranch> iDataContainer,
            SortTableTree iSortColumns, DrawTableTreeColumn<TBranch> iDrawColumns, int iFoldoutColIndex) : 
            base(iState, iHeader, iDataContainer, iSortColumns, iDrawColumns, iFoldoutColIndex) { }
    }
}
